use std::collections::HashSet;

/// strips `seg` as path segment suffix from `path`. 
/// 
/// `path` should not end with '/'. `seg` should not start with '/'. If `seg` is empty, `path` is returned.
/// The value returned does not end with '/'
fn strip_path_suffix<'t, 's>(path: &'t str, seg: &'s str) -> Option<&'t str> {
    if path == seg { 
        Some("") 
    } else if seg == "" { 
        Some(path)    
    } else if let Some(path) = path.strip_suffix(seg) {
        path.strip_suffix('/') 
    } else {
        None
    }
}

#[test]
fn test_path_suffix() {
    assert_eq!(Some("a/b"), strip_path_suffix("a/b", ""));
    assert_eq!(Some("a/b"), strip_path_suffix("a/b/index.html", "index.html"));
    assert_eq!(Some("a"), strip_path_suffix("a/b/index.html", "b/index.html"));
    assert_eq!(None, strip_path_suffix("a/b/index.html", "z/index.html"));
}

/// strips `seg` as path segment prefix from `path`.
/// 
/// `path` should not start with '/'. `seg` should not end with '/'. If `seg` is empty, `path` is returned.
/// The value returned does not start with '/'.
fn strip_path_prefix<'t, 's>(path: &'t str, seg: &'s str) -> Option<&'t str> {
    if path == seg { 
        Some("") 
    } else if seg == "" { 
        Some(path)
    } else if let Some(path) = path.strip_prefix(seg) {
        path.strip_prefix('/')
    } else {
        None
    }
}

#[test]
fn test_path_prefix() {
    assert_eq!(Some("index.html"), strip_path_prefix("index.html", ""));
    assert_eq!(Some("index.html"), strip_path_prefix("a/b/index.html", "a/b"));
    assert_eq!(None, strip_path_prefix("a/b/index.html", "a/z"));
}


#[derive(PartialEq, Debug)]
pub struct ServiceParts<'t> {
    /// no leading/trailing '/'
    root: &'t str,
    /// no leading/trailing '/'; `root` prefix stripped
    pub templates: Vec<&'t str>
}

impl<'t> ServiceParts<'t> {
    pub fn root_as_str(&self) -> &'t str {
        assert!(
            !(self.root.starts_with('/') || self.root.ends_with('/')), 
            "[ServiceParts::root_as_str] root must not start nor end with '/'"
        );
        self.root
    }

    pub fn root_as_path(&self) -> String {
        if self.root.is_empty() {
            "/".to_string()
        } else {
            "/".to_string() + self.root + "/"
        }
    }

    /// root as path, no trailing slash (when possible). Returns `None` for empty root
    pub fn root_as_path_nots(&self) -> Option<String> {
        if self.root.is_empty() {
            None
        } else {
            Some("/".to_string() + self.root)
        }
    }
}

pub type ServiceTree<'t> = Vec<ServiceParts<'t>>;

pub fn service_tree<'t>(template_names: HashSet<&'t str>, index_name: &str) -> ServiceTree<'t> {
    // select template names ending in "index.html" segment, stripping this segment
    let mut roots: Vec<&'t str> = template_names.iter().copied()
        .filter_map(|p| strip_path_suffix(p, index_name))
        .collect();

    // longest first
    roots.sort_by_key(|i| -(i.len() as isize));

    let rv = roots.into_iter().scan(template_names, |t, root| { 
        let mut t1 = HashSet::new();
        let mut templates = vec![];
        for p in t.drain() {
            if let Some(p) = strip_path_prefix(p, root) {
                templates.push(p);
            } else {
                t1.insert(p);
            }
        }
        *t = t1;
        templates.sort();
        Some(ServiceParts { root, templates })
    });

    rv.collect()
}


#[test]
fn test_service_tree() {
    assert_eq!(
        service_tree(
            ["index.html", "r.html", "a/index.html", "a/a.html", "z/z.html", "a/b/index.html"].into_iter().collect(), 
            "index.html"
        ),
        vec![
            ServiceParts { root: "a/b".into(), templates: vec!["index.html"] },
            ServiceParts { root: "a".into(), templates: vec!["a.html", "index.html"] },
            ServiceParts { root: "".into(), templates: vec!["index.html", "r.html", "z/z.html"] },
        ]
    );
}

